<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.0"><link rel="apple-touch-icon" sizes="180x180" href="/hexoblog/images/apple-touch-icon-next.png"><link rel="icon" type="image/png" sizes="32x32" href="/hexoblog/images/favicon-32x32-next.png"><link rel="icon" type="image/png" sizes="16x16" href="/hexoblog/images/favicon-16x16-next.png"><link rel="mask-icon" href="/hexoblog/images/logo.svg" color="#222"><link rel="stylesheet" href="/hexoblog/css/main.css"><link rel="stylesheet" href="https://unpkg.com/@fortawesome/fontawesome-free@5.15.3/css/all.min.css"><link rel="stylesheet" href="https://unpkg.com/animate.css@3.1.1/animate.min.css"><link rel="stylesheet" href="https://unpkg.com/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css"><script class="hexo-configurations">var NexT=window.NexT||{},CONFIG={hostname:"linqiankun.github.io",root:"/hexoblog/",images:"/hexoblog/images",scheme:"Gemini",version:"8.3.0",exturl:!1,sidebar:{position:"left",display:"post",padding:18,offset:12},copycode:!0,bookmark:{enable:!0,color:"#222",save:"auto"},fancybox:!0,mediumzoom:!1,lazyload:!1,pangu:!0,comments:{style:"tabs",active:null,storage:!0,lazyload:!1,nav:null},motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"fadeInDown",post_body:"fadeInDown",coll_header:"fadeInLeft",sidebar:"fadeInUp"}},prism:!1,i18n:{placeholder:"搜索...",empty:"没有找到任何搜索结果：${query}",hits_time:"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）",hits:"找到 ${hits} 个搜索结果"},path:"/hexoblog/search.xml",localsearch:{enable:!0,trigger:"auto",top_n_per_article:1,unescape:!1,preload:!1}}</script><meta name="description" content="介绍&amp;emsp;&amp;emsp;NameServer是一个非常简单的Topic路由注册中心，其角色类似Dubbo中的zookeeper，支持Broker的动态注册与发现。主要包括两个功能：Broker管理，NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制，检查Broker是否还存活；路由信息管理，每个NameServer将保存关于Broker集"><meta property="og:type" content="article"><meta property="og:title" content="rocketmq_NameServer启动流程"><meta property="og:url" content="https://linqiankun.github.io/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq-NameServer%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/index.html"><meta property="og:site_name" content="Study"><meta property="og:description" content="介绍&amp;emsp;&amp;emsp;NameServer是一个非常简单的Topic路由注册中心，其角色类似Dubbo中的zookeeper，支持Broker的动态注册与发现。主要包括两个功能：Broker管理，NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制，检查Broker是否还存活；路由信息管理，每个NameServer将保存关于Broker集"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="https://hexoimage.oss-cn-hangzhou.aliyuncs.com/hexobolg/java/nameServer.jpg"><meta property="article:published_time" content="2021-05-26T08:47:07.000Z"><meta property="article:modified_time" content="2024-05-14T07:19:44.315Z"><meta property="article:author" content="九分石人"><meta property="article:tag" content="消息中间件"><meta property="article:tag" content="rocketmq"><meta name="twitter:card" content="summary"><meta name="twitter:image" content="https://hexoimage.oss-cn-hangzhou.aliyuncs.com/hexobolg/java/nameServer.jpg"><link rel="canonical" href="https://linqiankun.github.io/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq-NameServer%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/"><script class="page-configurations">CONFIG.page={sidebar:"",isHome:!1,isPost:!0,lang:"zh-CN"}</script><title>rocketmq_NameServer启动流程 | Study</title><noscript><style>body{margin-top:2rem}.use-motion .collection-header,.use-motion .comments,.use-motion .menu-item,.use-motion .pagination,.use-motion .post-block,.use-motion .post-body,.use-motion .post-header,.use-motion .sidebar{visibility:visible}.use-motion .footer,.use-motion .header,.use-motion .site-brand-container .toggle{opacity:initial}.use-motion .custom-logo-image,.use-motion .site-subtitle,.use-motion .site-title{opacity:initial;top:initial}.use-motion .logo-line{transform:scaleX(1)}.search-pop-overlay,.sidebar-nav{display:none}.sidebar-panel{display:block}</style></noscript><link rel="alternate" href="/hexoblog/atom.xml" title="Study" type="application/atom+xml"><style>.darkmode--activated{--body-bg-color:#282828;--content-bg-color:#333;--card-bg-color:#555;--text-color:#ccc;--blockquote-color:#bbb;--link-color:#ccc;--link-hover-color:#eee;--brand-color:#ddd;--brand-hover-color:#ddd;--table-row-odd-bg-color:#282828;--table-row-hover-bg-color:#363636;--menu-item-bg-color:#555;--btn-default-bg:#222;--btn-default-color:#ccc;--btn-default-border-color:#555;--btn-default-hover-bg:#666;--btn-default-hover-color:#ccc;--btn-default-hover-border-color:#666;--highlight-background:#282b2e;--highlight-foreground:#a9b7c6;--highlight-gutter-background:#34393d;--highlight-gutter-foreground:#9ca9b6}.darkmode--activated img{opacity:.75}.darkmode--activated img:hover{opacity:.9}.darkmode--activated code{color:#69dbdc;background:0 0}button.darkmode-toggle{z-index:9999}.darkmode-ignore,img{display:flex!important}.beian img{display:inline-block!important}</style></head><body itemscope itemtype="http://schema.org/WebPage" class="use-motion"><div class="headband"></div><main class="main"><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-container"><div class="site-nav-toggle"><div class="toggle" aria-label="切换导航栏" role="button"><span class="toggle-line"></span> <span class="toggle-line"></span> <span class="toggle-line"></span></div></div><div class="site-meta"><a href="/hexoblog/" class="brand" rel="start"><i class="logo-line"></i><h1 class="site-title">Study</h1><i class="logo-line"></i></a><p class="site-subtitle" itemprop="description">好好学习，天天向上！</p></div><div class="site-nav-right"><div class="toggle popup-trigger"><i class="fa fa-search fa-fw fa-lg"></i></div></div></div><nav class="site-nav"><ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/hexoblog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/hexoblog/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/hexoblog/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/hexoblog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/hexoblog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li><li class="menu-item menu-item-sitemap"><a href="/hexoblog/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-search"><a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索</a></li></ul></nav><div class="search-pop-overlay"><div class="popup search-popup"><div class="search-header"><span class="search-icon"><i class="fa fa-search"></i></span><div class="search-input-container"><input autocomplete="off" autocapitalize="off" maxlength="80" placeholder="搜索..." spellcheck="false" type="search" class="search-input"></div><span class="popup-btn-close" role="button"><i class="fa fa-times-circle"></i></span></div><div class="search-result-container no-result"><div class="search-result-icon"><i class="fa fa-spinner fa-pulse fa-5x"></i></div></div></div></div></div><div class="toggle sidebar-toggle" role="button"><span class="toggle-line"></span> <span class="toggle-line"></span> <span class="toggle-line"></span></div><aside class="sidebar"><div class="sidebar-inner sidebar-nav-active sidebar-toc-active"><ul class="sidebar-nav"><li class="sidebar-nav-toc">文章目录</li><li class="sidebar-nav-overview">站点概览</li></ul><div class="sidebar-panel-container"><div class="post-toc-wrap sidebar-panel"><div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BB%8B%E7%BB%8D"><span class="nav-number">1.</span> <span class="nav-text">介绍</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%90%AF%E5%8A%A8"><span class="nav-number">2.</span> <span class="nav-text">启动</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84"><span class="nav-number">2.1.</span> <span class="nav-text">项目结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B"><span class="nav-number">2.2.</span> <span class="nav-text">启动过程</span></a></li></ol></li></ol></div></div><div class="site-overview-wrap sidebar-panel"><div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="site-author-image" itemprop="image" alt="九分石人" src="/hexoblog/images/avatar.png"><p class="site-author-name" itemprop="name">九分石人</p><div class="site-description" itemprop="description">my blogs</div></div><div class="site-state-wrap site-overview-item animated"><nav class="site-state"><div class="site-state-item site-state-posts"><a href="/hexoblog/archives/"><span class="site-state-item-count">239</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"><a href="/hexoblog/categories/"><span class="site-state-item-count">35</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"><a href="/hexoblog/tags/"><span class="site-state-item-count">64</span> <span class="site-state-item-name">标签</span></a></div></nav></div><div class="links-of-author site-overview-item animated"><span class="links-of-author-item"><a href="https://github.com/linqiankun" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;linqiankun" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a> </span><span class="links-of-author-item"><a href="linqiankun:191580378@qq.com" title="E-Mail → linqiankun:191580378@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a> </span><span class="links-of-author-item"><a href="https://weibo.com/jiufenshiren" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;jiufenshiren" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a> </span><span class="links-of-author-item"><a href="/hexoblog/atom.xml" title="Rss → &#x2F;atom.xml"><i class="fa fa-rss fa-fw"></i>Rss</a></span></div><div class="cc-license site-overview-item animated" itemprop="license"><a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh-CN" class="cc-opacity" rel="noopener" target="_blank"><img src="/hexoblog/images/cc-by-nc-sa.svg" alt="Creative Commons"></a></div><div class="links-of-blogroll site-overview-item animated"><div class="links-of-blogroll-title"><i class="fa fa-globe fa-fw"></i> Links</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"><a href="http://124.222.164.140:3001/workspace/hexoblog" title="http:&#x2F;&#x2F;124.222.164.140:3001&#x2F;workspace&#x2F;hexoblog" rel="noopener" target="_blank">AnythingLLM</a></li><li class="links-of-blogroll-item"><a href="https://www.yuque.com/jiufenshiren" title="https:&#x2F;&#x2F;www.yuque.com&#x2F;jiufenshiren" rel="noopener" target="_blank">YuQue</a></li><li class="links-of-blogroll-item"><a href="https://github.com/LinQiankun" title="https:&#x2F;&#x2F;github.com&#x2F;LinQiankun" rel="noopener" target="_blank">Github</a></li><li class="links-of-blogroll-item"><a href="https://gitee.com/linqiankun" title="https:&#x2F;&#x2F;gitee.com&#x2F;linqiankun" rel="noopener" target="_blank">Gitee</a></li><li class="links-of-blogroll-item"><a href="https://my.oschina.net/linqiankun" title="https:&#x2F;&#x2F;my.oschina.net&#x2F;linqiankun" rel="noopener" target="_blank">OsChina</a></li><li class="links-of-blogroll-item"><a href="https://segmentfault.com/u/jiufenshiren" title="https:&#x2F;&#x2F;segmentfault.com&#x2F;u&#x2F;jiufenshiren" rel="noopener" target="_blank">Segmentfault</a></li><li class="links-of-blogroll-item"><a href="https://juejin.cn/user/3263006243306205" title="https:&#x2F;&#x2F;juejin.cn&#x2F;user&#x2F;3263006243306205" rel="noopener" target="_blank">Juejin</a></li><li class="links-of-blogroll-item"><a href="https://blog.csdn.net/qq_36985536?spm=1010.2135.3001.5421" title="https:&#x2F;&#x2F;blog.csdn.net&#x2F;qq_36985536?spm&#x3D;1010.2135.3001.5421" rel="noopener" target="_blank">CSDN</a></li><li class="links-of-blogroll-item"><a href="https://www.cnblogs.com/jiufenshiren" title="https:&#x2F;&#x2F;www.cnblogs.com&#x2F;jiufenshiren" rel="noopener" target="_blank">CnBlogs</a></li><li class="links-of-blogroll-item"><a href="https://home.51cto.com/space?uid=12848310" title="https:&#x2F;&#x2F;home.51cto.com&#x2F;space?uid&#x3D;12848310" rel="noopener" target="_blank">51CTO</a></li></ul></div><script data-embed-id="94f790d6-f7ac-4d04-ad53-55d841525b5a" data-base-api-url="http://124.222.164.140:3001/api/embed" src="http://124.222.164.140:3001/embed/anythingllm-chat-widget.min.js"></script></div></div></div></aside><div class="sidebar-dimmer"></div></header><div class="back-to-top" role="button" aria-label="返回顶部"><i class="fa fa-arrow-up"></i> <span>0%</span></div><a role="button" class="book-mark-link book-mark-link-fixed"></a><noscript><div class="noscript-warning">Theme NexT works best with JavaScript enabled</div></noscript><div class="main-inner post posts-expand"><div class="post-block"><article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN"><link itemprop="mainEntityOfPage" href="https://linqiankun.github.io/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq-NameServer%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/hexoblog/images/avatar.png"><meta itemprop="name" content="九分石人"><meta itemprop="description" content="my blogs"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="Study"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">rocketmq_NameServer启动流程</h1><div class="post-meta-container"><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间：2021-05-26 16:47:07 16:47:07" itemprop="dateCreated datePublished" datetime="2021-05-26T16:47:07+08:00">2021-05-26 16:47:07</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar-check"></i> </span><span class="post-meta-item-text">更新于</span> <time title="修改时间：2024-05-14 15:19:44 15:19:44" itemprop="dateModified" datetime="2024-05-14T15:19:44+08:00">2024-05-14 15:19:44</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/hexoblog/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" itemprop="url" rel="index"><span itemprop="name">消息中间件</span></a> </span>， <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/hexoblog/categories/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/" itemprop="url" rel="index"><span itemprop="name">rocketmq</span></a> </span></span><span class="post-meta-item" title="阅读次数" id="busuanzi_container_page_pv" style="display:none"><span class="post-meta-item-icon"><i class="far fa-eye"></i> </span><span class="post-meta-item-text">阅读次数：</span> <span id="busuanzi_value_page_pv"></span></span></div><div class="post-meta"><span class="post-meta-item" title="本文字数"><span class="post-meta-item-icon"><i class="far fa-file-word"></i> </span><span class="post-meta-item-text">本文字数：</span> <span>14k</span> </span><span class="post-meta-item" title="阅读时长"><span class="post-meta-item-icon"><i class="far fa-clock"></i> </span><span class="post-meta-item-text">阅读时长 &asymp;</span> <span>13 分钟</span></span></div></div></header><div class="post-body" itemprop="articleBody"><h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>&emsp;&emsp;NameServer是一个非常简单的Topic路由注册中心，其角色类似Dubbo中的zookeeper，支持Broker的动态注册与发现。主要包括两个功能：Broker管理，NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制，检查Broker是否还存活；路由信息管理，每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息，从而进行消息的投递和消费。</p><span id="more"></span><hr><h1 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h1><h2 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h2><p>&emsp;&emsp;看看项目结构</p><p>&emsp;&emsp;<img src="https://hexoimage.oss-cn-hangzhou.aliyuncs.com/hexobolg/java/nameServer.jpg" alt="项目结构"></p><ul><li>KVConfigManager：nameServer的KV配置管理器，这里加载NameSrv的启动配置。</li><li>KVConfigSerializeWrapper：kv配置的存储位置，每一个kv配置都会被解析存储进入一个该对象中。</li><li>ClusterTestRequestProcessor：暂时忽略。</li><li>DefaultRequestProcessor：默认的请求处理器，里面定义了对broker，topic等的一系列操作等。</li><li>BrokerHousekeepingService：</li><li>RouteInfoManager：路由信息管理类，里面存放了broker的状态信息和broker及topic的关联信息。</li><li>NamesrvController：nameServer的控制类，管理NameServer的启动，初始化，停止等生命周期。</li><li>NamesrvStartup：nameServer的启动入口。</li></ul><h2 id="启动过程"><a href="#启动过程" class="headerlink" title="启动过程"></a>启动过程</h2><p>&emsp;&emsp;启动从NamesrvStartup开始。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 从main方法进入走到了这里</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> NamesrvController <span class="title">main0</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 闯将启动控制器NameServerController</span></span><br><span class="line">        NamesrvController controller = createNamesrvController(args);</span><br><span class="line">        <span class="comment">// 启动NameServer</span></span><br><span class="line">        start(controller);</span><br><span class="line">        String tip = <span class="string">&quot;The Name Server boot success. serializeType=&quot;</span> + RemotingCommand.getSerializeTypeConfigInThisServer();</span><br><span class="line">        log.info(tip);</span><br><span class="line">        System.out.printf(<span class="string">&quot;%s%n&quot;</span>, tip);</span><br><span class="line">        <span class="keyword">return</span> controller;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        System.exit(-<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;createNamesrvController中主要就是构造参数创建控制器。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这个方法中主要进入NameServerController的构造，会添加很多参数</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> NamesrvController <span class="title">createNamesrvController</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, JoranException </span>&#123;</span><br><span class="line">    System.setProperty(RemotingCommand.REMOTING_VERSION_KEY, Integer.toString(MQVersion.CURRENT_VERSION));</span><br><span class="line">    <span class="comment">//PackageConflictDetect.detectFastjson();</span></span><br><span class="line"></span><br><span class="line">    Options options = ServerUtil.buildCommandlineOptions(<span class="keyword">new</span> Options());</span><br><span class="line">    <span class="comment">// 这里解析手动启动时传入的命令行参数</span></span><br><span class="line">    commandLine = ServerUtil.parseCmdLine(<span class="string">&quot;mqnamesrv&quot;</span>, args, buildCommandlineOptions(options), <span class="keyword">new</span> PosixParser());</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">null</span> == commandLine) &#123;</span><br><span class="line">        System.exit(-<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义NameServer和使用到的Netty的配置</span></span><br><span class="line">    <span class="keyword">final</span> NamesrvConfig namesrvConfig = <span class="keyword">new</span> NamesrvConfig();</span><br><span class="line">    <span class="keyword">final</span> NettyServerConfig nettyServerConfig = <span class="keyword">new</span> NettyServerConfig();</span><br><span class="line">    <span class="comment">// 监听端口</span></span><br><span class="line">    nettyServerConfig.setListenPort(<span class="number">9876</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理-c参数后的参数</span></span><br><span class="line">    <span class="keyword">if</span> (commandLine.hasOption(<span class="string">&#x27;c&#x27;</span>)) &#123;</span><br><span class="line">        。。。</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理-p参数后的参数</span></span><br><span class="line">    <span class="keyword">if</span> (commandLine.hasOption(<span class="string">&#x27;p&#x27;</span>)) &#123;</span><br><span class="line">        。。。</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 将参数转换位NameServConfig对象，这是Common包的定义的类</span></span><br><span class="line">    MixAll.properties2Object(ServerUtil.commandLine2Properties(commandLine), namesrvConfig);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">null</span> == namesrvConfig.getRocketmqHome()) &#123;</span><br><span class="line">        System.out.printf(<span class="string">&quot;Please set the %s variable in your environment to match the location of the RocketMQ installation%n&quot;</span>, MixAll.ROCKETMQ_HOME_ENV);</span><br><span class="line">        System.exit(-<span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</span><br><span class="line">    JoranConfigurator configurator = <span class="keyword">new</span> JoranConfigurator();</span><br><span class="line">    configurator.setContext(lc);</span><br><span class="line">    lc.reset();</span><br><span class="line">    configurator.doConfigure(namesrvConfig.getRocketmqHome() + <span class="string">&quot;/conf/logback_namesrv.xml&quot;</span>);</span><br><span class="line"></span><br><span class="line">    log = InternalLoggerFactory.getLogger(LoggerName.NAMESRV_LOGGER_NAME);</span><br><span class="line"></span><br><span class="line">    MixAll.printObjectProperties(log, namesrvConfig);</span><br><span class="line">    MixAll.printObjectProperties(log, nettyServerConfig);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建控制器，使用上面构造的参数</span></span><br><span class="line">    <span class="keyword">final</span> NamesrvController controller = <span class="keyword">new</span> NamesrvController(namesrvConfig, nettyServerConfig);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// remember all configs to prevent discard</span></span><br><span class="line">    controller.getConfiguration().registerConfig(properties);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> controller;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;start方法中，会初始化控制器，如果初始化失败，就会关闭，后面在进行启动，最终调用controller中的Start方法。<br>&emsp;&emsp;controller中的Start方法，直接调用了romoting模块中netty的启动方法，打开端口，启动netty服务。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// NamesrvStartup.start</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> NamesrvController <span class="title">start</span><span class="params">(<span class="keyword">final</span> NamesrvController controller)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">null</span> == controller) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;NamesrvController is null&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">boolean</span> initResult = controller.initialize();</span><br><span class="line">    <span class="keyword">if</span> (!initResult) &#123;</span><br><span class="line">        controller.shutdown();</span><br><span class="line">        System.exit(-<span class="number">3</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    Runtime.getRuntime().addShutdownHook(<span class="keyword">new</span> ShutdownHookThread(log, <span class="keyword">new</span> Callable&lt;Void&gt;() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> Void <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">            controller.shutdown();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;));</span><br><span class="line"></span><br><span class="line">    controller.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> controller;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">NamesrvController.start</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.remotingServer.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.fileWatchService != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">this</span>.fileWatchService.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;主要流程看Controller的initialize方法，看看做了什么。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">initialize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 加载kv配置</span></span><br><span class="line">    <span class="keyword">this</span>.kvConfigManager.load();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">this</span>.remotingServer = <span class="keyword">new</span> NettyRemotingServer(<span class="keyword">this</span>.nettyServerConfig, <span class="keyword">this</span>.brokerHousekeepingService);</span><br><span class="line">    <span class="comment">// 初始化线程池</span></span><br><span class="line">    <span class="keyword">this</span>.remotingExecutor =</span><br><span class="line">        Executors.newFixedThreadPool(nettyServerConfig.getServerWorkerThreads(), <span class="keyword">new</span> ThreadFactoryImpl(<span class="string">&quot;RemotingExecutorThread_&quot;</span>));</span><br><span class="line">    <span class="comment">// 注册请求处理器</span></span><br><span class="line">    <span class="keyword">this</span>.registerProcessor();</span><br><span class="line">    <span class="comment">// 这里面采用了心跳机制，去检测broker的存活，定时线程池去，扫描所有broker。</span></span><br><span class="line">    <span class="keyword">this</span>.scheduledExecutorService.scheduleAtFixedRate(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            NamesrvController.<span class="keyword">this</span>.routeInfoManager.scanNotActiveBroker();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">5</span>, <span class="number">10</span>, TimeUnit.SECONDS);</span><br><span class="line">    <span class="comment">// 定时将所有的broker和topic信息打出到日志。</span></span><br><span class="line">    <span class="keyword">this</span>.scheduledExecutorService.scheduleAtFixedRate(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            NamesrvController.<span class="keyword">this</span>.kvConfigManager.printAllPeriodically();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="number">1</span>, <span class="number">10</span>, TimeUnit.MINUTES);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (TlsSystemConfig.tlsMode != TlsMode.DISABLED) &#123;</span><br><span class="line">        <span class="comment">// Register a listener to reload SslContext</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            fileWatchService = <span class="keyword">new</span> FileWatchService(</span><br><span class="line">                <span class="keyword">new</span> String[] &#123;</span><br><span class="line">                    TlsSystemConfig.tlsServerCertPath,</span><br><span class="line">                    TlsSystemConfig.tlsServerKeyPath,</span><br><span class="line">                    TlsSystemConfig.tlsServerTrustCertPath</span><br><span class="line">                &#125;,</span><br><span class="line">                <span class="keyword">new</span> FileWatchService.Listener() &#123;</span><br><span class="line">                    <span class="keyword">boolean</span> certChanged, keyChanged = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="meta">@Override</span></span><br><span class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onChanged</span><span class="params">(String path)</span> </span>&#123;</span><br><span class="line">                        <span class="keyword">if</span> (path.equals(TlsSystemConfig.tlsServerTrustCertPath)) &#123;</span><br><span class="line">                            log.info(<span class="string">&quot;The trust certificate changed, reload the ssl context&quot;</span>);</span><br><span class="line">                            reloadServerSslContext();</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (path.equals(TlsSystemConfig.tlsServerCertPath)) &#123;</span><br><span class="line">                            certChanged = <span class="keyword">true</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (path.equals(TlsSystemConfig.tlsServerKeyPath)) &#123;</span><br><span class="line">                            keyChanged = <span class="keyword">true</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (certChanged &amp;&amp; keyChanged) &#123;</span><br><span class="line">                            log.info(<span class="string">&quot;The certificate and private key changed, reload the ssl context&quot;</span>);</span><br><span class="line">                            certChanged = keyChanged = <span class="keyword">false</span>;</span><br><span class="line">                            reloadServerSslContext();</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">reloadServerSslContext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                        ((NettyRemotingServer) remotingServer).loadSslContext();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.warn(<span class="string">&quot;FileWatchService created error, can&#x27;t load the certificate dynamically&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;看看注册的请求处理器，在默认的请求处理器中定义了系列的处理方法，会根据不同的请求进行不同的处理。这些方法中大多调用了Controller中的方法，Controller中定义了路由信息管理器，KV配置管理器等。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Controller.registerProcessor</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">registerProcessor</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (namesrvConfig.isClusterTest()) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.remotingServer.registerDefaultProcessor(<span class="keyword">new</span> ClusterTestRequestProcessor(<span class="keyword">this</span>, namesrvConfig.getProductEnvName()),</span><br><span class="line">            <span class="keyword">this</span>.remotingExecutor);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 默认注册default的请求处理器</span></span><br><span class="line">        <span class="keyword">this</span>.remotingServer.registerDefaultProcessor(<span class="keyword">new</span> DefaultRequestProcessor(<span class="keyword">this</span>), <span class="keyword">this</span>.remotingExecutor);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// DefaultRequestProcessor.processRequest</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> RemotingCommand <span class="title">processRequest</span><span class="params">(ChannelHandlerContext ctx,</span></span></span><br><span class="line"><span class="function"><span class="params">    RemotingCommand request)</span> <span class="keyword">throws</span> RemotingCommandException </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (ctx != <span class="keyword">null</span>) &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;receive request, &#123;&#125; &#123;&#125; &#123;&#125;&quot;</span>,</span><br><span class="line">            request.getCode(),</span><br><span class="line">            RemotingHelper.parseChannelRemoteAddr(ctx.channel()),</span><br><span class="line">            request);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 根据不同请求进行不同的处理</span></span><br><span class="line">    <span class="keyword">switch</span> (request.getCode()) &#123;</span><br><span class="line">        <span class="keyword">case</span> RequestCode.PUT_KV_CONFIG:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.putKVConfig(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_KV_CONFIG:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getKVConfig(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.DELETE_KV_CONFIG:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.deleteKVConfig(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.QUERY_DATA_VERSION:</span><br><span class="line">            <span class="keyword">return</span> queryBrokerTopicConfig(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.REGISTER_BROKER:</span><br><span class="line">            Version brokerVersion = MQVersion.value2Version(request.getVersion());</span><br><span class="line">            <span class="keyword">if</span> (brokerVersion.ordinal() &gt;= MQVersion.Version.V3_0_11.ordinal()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">this</span>.registerBrokerWithFilterServer(ctx, request);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">this</span>.registerBroker(ctx, request);</span><br><span class="line">            &#125;</span><br><span class="line">        <span class="keyword">case</span> RequestCode.UNREGISTER_BROKER:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.unregisterBroker(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_ROUTEINFO_BY_TOPIC:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getRouteInfoByTopic(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_BROKER_CLUSTER_INFO:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getBrokerClusterInfo(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.WIPE_WRITE_PERM_OF_BROKER:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.wipeWritePermOfBroker(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_ALL_TOPIC_LIST_FROM_NAMESERVER:</span><br><span class="line">            <span class="keyword">return</span> getAllTopicListFromNameserver(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.DELETE_TOPIC_IN_NAMESRV:</span><br><span class="line">            <span class="keyword">return</span> deleteTopicInNamesrv(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_KVLIST_BY_NAMESPACE:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getKVListByNamespace(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_TOPICS_BY_CLUSTER:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getTopicsByCluster(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_NS:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getSystemTopicListFromNs(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_UNIT_TOPIC_LIST:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getUnitTopicList(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_HAS_UNIT_SUB_TOPIC_LIST:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getHasUnitSubTopicList(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_HAS_UNIT_SUB_UNUNIT_TOPIC_LIST:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getHasUnitSubUnUnitTopicList(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.UPDATE_NAMESRV_CONFIG:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.updateConfig(ctx, request);</span><br><span class="line">        <span class="keyword">case</span> RequestCode.GET_NAMESRV_CONFIG:</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">this</span>.getConfig(ctx, request);</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;Controller的initialize方法中，处理上面注册的处理器外，还有进行心跳检测的周期性定时线程池。从scanNotActiveBroker方法进入。<br>&emsp;&emsp;调度线程定时的扫描brokerLiveTable里面broker的状态信息，发现最近的更新时间与当前时间相差大于BROKER_CHANNEL_EXPIRED_TIME =1000 *60 *2，即两分钟的话，就删除当前broker。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">scanNotActiveBroker</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 遍历brokerLiveTable</span></span><br><span class="line">    Iterator&lt;Entry&lt;String, BrokerLiveInfo&gt;&gt; it = <span class="keyword">this</span>.brokerLiveTable.entrySet().iterator();</span><br><span class="line">    <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">        Entry&lt;String, BrokerLiveInfo&gt; next = it.next();</span><br><span class="line">        <span class="keyword">long</span> last = next.getValue().getLastUpdateTimestamp();</span><br><span class="line">        <span class="comment">// 判断更新时间和当前时间的差值</span></span><br><span class="line">        <span class="keyword">if</span> ((last + BROKER_CHANNEL_EXPIRED_TIME) &lt; System.currentTimeMillis()) &#123;</span><br><span class="line">            RemotingUtil.closeChannel(next.getValue().getChannel());</span><br><span class="line">            <span class="comment">// 移除</span></span><br><span class="line">            it.remove();</span><br><span class="line">            log.warn(<span class="string">&quot;The broker channel expired, &#123;&#125; &#123;&#125;ms&quot;</span>, next.getKey(), BROKER_CHANNEL_EXPIRED_TIME);</span><br><span class="line">            <span class="comment">// 删除broker</span></span><br><span class="line">            <span class="keyword">this</span>.onChannelDestroy(next.getKey(), next.getValue().getChannel());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&emsp;&emsp;删除broker就是删除RouteInfoManager中维护的几个hashmap中关于broker的信息。onChannelDestroy方法中删除了broker。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onChannelDestroy</span><span class="params">(String remoteAddr, Channel channel)</span> </span>&#123;</span><br><span class="line">    String brokerAddrFound = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">if</span> (channel != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.lock.readLock().lockInterruptibly();</span><br><span class="line">                Iterator&lt;Entry&lt;String, BrokerLiveInfo&gt;&gt; itBrokerLiveTable =</span><br><span class="line">                    <span class="keyword">this</span>.brokerLiveTable.entrySet().iterator();</span><br><span class="line">                <span class="keyword">while</span> (itBrokerLiveTable.hasNext()) &#123;</span><br><span class="line">                    Entry&lt;String, BrokerLiveInfo&gt; entry = itBrokerLiveTable.next();</span><br><span class="line">                    <span class="keyword">if</span> (entry.getValue().getChannel() == channel) &#123;</span><br><span class="line">                        brokerAddrFound = entry.getKey();</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.lock.readLock().unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;onChannelDestroy Exception&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">null</span> == brokerAddrFound) &#123;</span><br><span class="line">        brokerAddrFound = remoteAddr;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;the broker&#x27;s channel destroyed, &#123;&#125;, clean it&#x27;s data structure at once&quot;</span>, brokerAddrFound);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (brokerAddrFound != <span class="keyword">null</span> &amp;&amp; brokerAddrFound.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.lock.writeLock().lockInterruptibly();</span><br><span class="line">                <span class="keyword">this</span>.brokerLiveTable.remove(brokerAddrFound);</span><br><span class="line">                <span class="keyword">this</span>.filterServerTable.remove(brokerAddrFound);</span><br><span class="line">                String brokerNameFound = <span class="keyword">null</span>;</span><br><span class="line">                <span class="keyword">boolean</span> removeBrokerName = <span class="keyword">false</span>;</span><br><span class="line">                Iterator&lt;Entry&lt;String, BrokerData&gt;&gt; itBrokerAddrTable =</span><br><span class="line">                    <span class="keyword">this</span>.brokerAddrTable.entrySet().iterator();</span><br><span class="line">                <span class="keyword">while</span> (itBrokerAddrTable.hasNext() &amp;&amp; (<span class="keyword">null</span> == brokerNameFound)) &#123;</span><br><span class="line">                    BrokerData brokerData = itBrokerAddrTable.next().getValue();</span><br><span class="line"></span><br><span class="line">                    Iterator&lt;Entry&lt;Long, String&gt;&gt; it = brokerData.getBrokerAddrs().entrySet().iterator();</span><br><span class="line">                    <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">                        Entry&lt;Long, String&gt; entry = it.next();</span><br><span class="line">                        Long brokerId = entry.getKey();</span><br><span class="line">                        String brokerAddr = entry.getValue();</span><br><span class="line">                        <span class="keyword">if</span> (brokerAddr.equals(brokerAddrFound)) &#123;</span><br><span class="line">                            brokerNameFound = brokerData.getBrokerName();</span><br><span class="line">                            it.remove();</span><br><span class="line">                            log.info(<span class="string">&quot;remove brokerAddr[&#123;&#125;, &#123;&#125;] from brokerAddrTable, because channel destroyed&quot;</span>,</span><br><span class="line">                                brokerId, brokerAddr);</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (brokerData.getBrokerAddrs().isEmpty()) &#123;</span><br><span class="line">                        removeBrokerName = <span class="keyword">true</span>;</span><br><span class="line">                        itBrokerAddrTable.remove();</span><br><span class="line">                        log.info(<span class="string">&quot;remove brokerName[&#123;&#125;] from brokerAddrTable, because channel destroyed&quot;</span>,</span><br><span class="line">                            brokerData.getBrokerName());</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (brokerNameFound != <span class="keyword">null</span> &amp;&amp; removeBrokerName) &#123;</span><br><span class="line">                    Iterator&lt;Entry&lt;String, Set&lt;String&gt;&gt;&gt; it = <span class="keyword">this</span>.clusterAddrTable.entrySet().iterator();</span><br><span class="line">                    <span class="keyword">while</span> (it.hasNext()) &#123;</span><br><span class="line">                        Entry&lt;String, Set&lt;String&gt;&gt; entry = it.next();</span><br><span class="line">                        String clusterName = entry.getKey();</span><br><span class="line">                        Set&lt;String&gt; brokerNames = entry.getValue();</span><br><span class="line">                        <span class="keyword">boolean</span> removed = brokerNames.remove(brokerNameFound);</span><br><span class="line">                        <span class="keyword">if</span> (removed) &#123;</span><br><span class="line">                            log.info(<span class="string">&quot;remove brokerName[&#123;&#125;], clusterName[&#123;&#125;] from clusterAddrTable, because channel destroyed&quot;</span>,</span><br><span class="line">                                brokerNameFound, clusterName);</span><br><span class="line"></span><br><span class="line">                            <span class="keyword">if</span> (brokerNames.isEmpty()) &#123;</span><br><span class="line">                                log.info(<span class="string">&quot;remove the clusterName[&#123;&#125;] from clusterAddrTable, because channel destroyed and no broker in this cluster&quot;</span>,</span><br><span class="line">                                    clusterName);</span><br><span class="line">                                it.remove();</span><br><span class="line">                            &#125;</span><br><span class="line"></span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (removeBrokerName) &#123;</span><br><span class="line">                    Iterator&lt;Entry&lt;String, List&lt;QueueData&gt;&gt;&gt; itTopicQueueTable =</span><br><span class="line">                        <span class="keyword">this</span>.topicQueueTable.entrySet().iterator();</span><br><span class="line">                    <span class="keyword">while</span> (itTopicQueueTable.hasNext()) &#123;</span><br><span class="line">                        Entry&lt;String, List&lt;QueueData&gt;&gt; entry = itTopicQueueTable.next();</span><br><span class="line">                        String topic = entry.getKey();</span><br><span class="line">                        List&lt;QueueData&gt; queueDataList = entry.getValue();</span><br><span class="line"></span><br><span class="line">                        Iterator&lt;QueueData&gt; itQueueData = queueDataList.iterator();</span><br><span class="line">                        <span class="keyword">while</span> (itQueueData.hasNext()) &#123;</span><br><span class="line">                            QueueData queueData = itQueueData.next();</span><br><span class="line">                            <span class="keyword">if</span> (queueData.getBrokerName().equals(brokerNameFound)) &#123;</span><br><span class="line">                                itQueueData.remove();</span><br><span class="line">                                log.info(<span class="string">&quot;remove topic[&#123;&#125; &#123;&#125;], from topicQueueTable, because channel destroyed&quot;</span>,</span><br><span class="line">                                    topic, queueData);</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">if</span> (queueDataList.isEmpty()) &#123;</span><br><span class="line">                            itTopicQueueTable.remove();</span><br><span class="line">                            log.info(<span class="string">&quot;remove topic[&#123;&#125;] all queue, from topicQueueTable, because channel destroyed&quot;</span>,</span><br><span class="line">                                topic);</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="keyword">this</span>.lock.writeLock().unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;onChannelDestroy Exception&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><div class="popular-posts-header">相关文章</div><ul class="popular-posts"><li class="popular-posts-item"><div class="popular-posts-title"><a href="\hexoblog\md\消息中间件\rocketmq和kafka的区别\" rel="bookmark">rocketmq和kafka的区别</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="\hexoblog\md\消息中间件\rocketmq\MQ消息幂等\" rel="bookmark">MQ消息幂等</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="\hexoblog\md\消息中间件\rocketmq\rocketmq概念总结\" rel="bookmark">rocketmq概念总结</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="\hexoblog\md\消息中间件\rocketmq\rocketmq消息分类\" rel="bookmark">rocketmq消息分类</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="\hexoblog\md\消息中间件\rocketmq\rocketmq消息可靠性\" rel="bookmark">rocketmq消息可靠性</a></div></li></ul><footer class="post-footer"><div class="post-copyright"><ul><li class="post-copyright-author"><strong>本文作者： </strong>九分石人</li><li class="post-copyright-link"><strong>本文链接：</strong> <a href="https://linqiankun.github.io/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq-NameServer%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/" title="rocketmq_NameServer启动流程">https://linqiankun.github.io/hexoblog/md/消息中间件/rocketmq/rocketmq-NameServer启动流程/</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/zh-CN" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！</li></ul></div><div class="followme"><span>欢迎关注我的其它发布渠道</span><div class="social-list"><div class="social-item"><a target="_blank" class="social-link" href="https://github.com/linqiankun"><span class="icon"><i class="fab fa-github"></i> </span><span class="label">GitHub</span></a></div><div class="social-item"><a target="_blank" class="social-link" href="https://gitee.com/linqiankun"><span class="icon"><i class="fab fa-git"></i> </span><span class="label">Gitee</span></a></div><div class="social-item"><a target="_blank" class="social-link" href="/hexoblog/images/thefive.jpg"><span class="icon"><i class="fab fa-weixin"></i> </span><span class="label">WeChat</span></a></div><div class="social-item"><a target="_blank" class="social-link" href="/hexoblog/atom.xml"><span class="icon"><i class="fa fa-rss"></i> </span><span class="label">RSS</span></a></div></div></div><div class="post-tags"><a href="/hexoblog/tags/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/" rel="tag"><i class="fa fa-tag"></i> 消息中间件</a> <a href="/hexoblog/tags/rocketmq/" rel="tag"><i class="fa fa-tag"></i> rocketmq</a></div><div class="post-nav"><div class="post-nav-item"><a href="/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq%E7%94%9F%E4%BA%A7%E6%B6%88%E8%B4%B9%E6%B5%81%E7%A8%8B/" rel="prev" title="rocketmq生产消费流程"><i class="fa fa-chevron-left"></i> rocketmq生产消费流程</a></div><div class="post-nav-item"><a href="/hexoblog/md/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/rocketmq/rocketmq%E6%B6%88%E6%81%AF%E5%88%86%E7%B1%BB/" rel="next" title="rocketmq消息分类">rocketmq消息分类 <i class="fa fa-chevron-right"></i></a></div></div></footer></article></div><script>window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      const activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      const commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }</script></div></main><footer class="footer"><div class="footer-inner"><div class="copyright">&copy; Tue Mar 02 2021 08:00:00 GMT+0800 (中国标准时间) – <span itemprop="copyrightYear">2025</span> <span class="with-love"><i class="fa fa-heart"></i> </span><span class="author" itemprop="copyrightHolder">九分石人</span></div><div class="wordcount"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="fa fa-chart-line"></i> </span><span title="站点总字数">682k</span> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="fa fa-coffee"></i> </span><span title="站点阅读时长">10:20</span></span></div><div class="busuanzi-count"><span class="post-meta-item" id="busuanzi_container_site_uv" style="display:none"><span class="post-meta-item-icon"><i class="fa fa-user"></i> </span><span class="site-uv" title="总访客量"><span id="busuanzi_value_site_uv"></span> </span></span><span class="post-meta-item" id="busuanzi_container_site_pv" style="display:none"><span class="post-meta-item-icon"><i class="fa fa-eye"></i> </span><span class="site-pv" title="总访问量"><span id="busuanzi_value_site_pv"></span></span></span></div></div></footer><script size="300" alpha="0.6" zindex="-1" src="https://unpkg.com/ribbon.js@1.0.2/dist/ribbon.min.js"></script><script src="https://unpkg.com/animejs@3.2.1/lib/anime.min.js"></script><script src="https://unpkg.com/jquery@3.6.0/dist/jquery.min.js"></script><script src="https://unpkg.com/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script><script src="https://unpkg.com/pangu@4.0.7/dist/browser/pangu.min.js"></script><script src="/hexoblog/js/utils.js"></script><script src="/hexoblog/js/motion.js"></script><script src="/hexoblog/js/next-boot.js"></script><script src="/hexoblog/js/bookmark.js"></script><script src="/hexoblog/js/local-search.js"></script><script>var mermaidElements = document.querySelectorAll('.mermaid');
if (mermaidElements.length) {
  NexT.utils.getScript('https://unpkg.com/mermaid@8.9.2/dist/mermaid.min.js', () => {
    mermaidElements.forEach(element => {
      const newElement = document.createElement('div');
      newElement.innerHTML = element.innerHTML;
      newElement.className = element.className;
      element.parentNode.replaceChild(newElement, element);
    });

    mermaid.init({
      theme    : 'default',
      logLevel : 3,
      flowchart: { curve     : 'linear' },
      gantt    : { axisFormat: '%m/%d/%Y' },
      sequence : { actorMargin: 50 }
    }, '.mermaid');
  }, window.mermaid);
}</script><script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><script src="https://unpkg.com/darkmode-js@1.5.7/lib/darkmode-js.min.js"></script><script>var options = {
  bottom: '64px',
  right: 'unset',
  left: '32px',
  time: '0.5s',
  mixColor: 'transparent',
  backgroundColor: 'transparent',
  buttonColorDark: '#100f2c',
  buttonColorLight: '#fff',
  saveInCookies: true,
  label: '🌓',
  autoMatchOsTheme: true
}
const darkmode = new Darkmode(options);
window.darkmode = darkmode;
darkmode.showWidget();</script></body></html>